//
//  PSGame.h
//  PrefCore
//
//  Created by Sergey on 1/16/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "PSGameSettings.h"
#import "PSGameInputOptions.h"
#import "PSHand.h"
#import "PSPlayer.h"
#import "PSBid.h"
#import "PSScore.h"
#import "PSStatistics.h"

#define PS_GAME_PLAYERS 3
#define PS_GAME_PLAYERS 3
#define PS_TALON_SIZE 2
#define PS_MAXIMUM_TRICKS 10
#define PS_MAXIMUM_ROUNDS 10
#define PS_HAND_SIZE 10

typedef enum {
    kMovingTypeUnknown
,   kMovingTypeOpen
,   kMovingTypeStanding
} PSGameMovingType;

inline PSPlayerId PSGameNextPlayerId(PSPlayerId playerId) {
    return (playerId + 1) % PS_GAME_PLAYERS;
}

inline PSPlayerId PSGamePreviousPlayerId(PSPlayerId playerId) {
    return (playerId == 0) ? PS_GAME_PLAYERS - 1 : playerId-1;
}

/// Represents the current state of the bidding process.
@interface PSGameBidData : NSObject

@property (nonatomic, readwrite) PSBid *current;

@property (nonatomic, readwrite) PSBid *max;

@property (nonatomic, readwrite) int count;

@property (nonatomic, readwrite) BOOL isVisible;

-(void)reset;

@end

/// Represents the current and initial hands.
@interface PSHandData : NSObject

@property (nonatomic, readwrite) PSHandHash initial;

@property (nonatomic, readwrite) PSHandHash current;

@property (nonatomic, readwrite) BOOL open;

-(void)reset:(BOOL)open;

@end

@interface PSTrickData : NSObject

@property (nonatomic, readonly) PSTrickHash last;

@property (nonatomic, readwrite) PSTrickHash current;

@property (nonatomic, readonly) NSArray *listOfTricks;

-(void)reset;

-(PSHandHash)trashCurrentWithOldTrash:(PSHandHash)oldTrash;

-(PSTrickCount)tricksOfPlayer:(PSPlayerId)playerId withTrump:(PSCardSuit)trump;

@end

#define PS_NO_PLAYER_ID ((PSPlayerId)-1)

/// Player data for serialization.
@interface PSPlayerData : NSObject

/// Game dealer.
@property (nonatomic, readwrite) PSPlayerId dealer;

/// Starting player and hand (player can play other player's hand).
@property (nonatomic, readwrite) PSPlayerId starting;

/// Highest bidder - determined in contract mode (or null if going to play passing).
@property (nonatomic, readwrite) PSPlayerId declarer;

/// Player who plays against the declarer (the main whister).
@property (nonatomic, readwrite) PSPlayerId opponent;

/// Player who plays against the declarer (the passer).
@property (nonatomic, readwrite) PSPlayerId passer;

/// Player declared half whist.
@property (nonatomic, readwrite) PSPlayerId half;

/// Current player and hand (player can play other player's hand).
@property (nonatomic, readwrite) PSPlayerId current;

/// Cards in player possession (hash for each suit).
@property (nonatomic, readonly) PSHandData * __strong *hands;

/// Last and max bids - becomes contract after contracting mode.
@property (nonatomic, readonly) PSGameBidData * __strong *bids;

/// Player scores.
@property (nonatomic, readonly) PSScore * __strong *scores;

/// Tricks taken so far in the current set.
@property (nonatomic, readonly) int* tricks;

-(void)reset:(PSPlayerId)identifier open:(BOOL)open;

@end

@interface PSGame : NSObject

@property (nonatomic, readonly) PSTrickData *trick;

@property (nonatomic, readwrite) int rounds;

@property (nonatomic, readwrite) int hand;

@property (nonatomic, readwrite) PSBid *lastBid;

@property (nonatomic, readwrite) PSGameMovingType movingTye;

@property (nonatomic, readonly) NSArray *players;

@property (nonatomic, readonly) uint32_t consecutivePassingSets;

/// Get declarer player number of tricks.
@property (nonatomic, readonly) PSTrickCount declarerTricks;

@property (nonatomic, readonly) PSPlayer *dealer;

@property (nonatomic, readonly) PSPlayer *declarer;

@property (nonatomic, readonly) PSPlayer *opponent;

@property (nonatomic, readonly) PSPlayer *passer;

@property (nonatomic, readonly) PSPlayer *half;

@property (nonatomic, readonly) PSPlayer *starting;

@property (nonatomic, readonly) PSPlayer *current;

/// Get declarer player hand.
@property (nonatomic, readonly) PSHandData *declarerHand;

/// Get human player hand.
@property (nonatomic, readonly) PSHandData *humanHand;

/// Get current player hand.
@property (nonatomic, readonly) PSHandData *currentPlayerHand;

/// Get opponent player hand.
@property (nonatomic, readonly) PSHandData *opponentHand;

/// Get passer player hand.
@property (nonatomic, readonly) PSHandData *passerHand;

/// Get player hand.
-(PSHandData*) handForPlayerId:(PSPlayerId)identifier;

/// Get declarer player bid.
@property (nonatomic, readonly) PSGameBidData *declarerBid;

/// Get human player bid.
@property (nonatomic, readonly) PSGameBidData *humanBid;

/// Get current player bid.
@property (nonatomic, readonly) PSGameBidData *currentPlayerBid;

/// Get half-whist player bid.
@property (nonatomic, readonly) PSGameBidData *halfBid;

/// Get passer player bid.
@property (nonatomic, readonly) PSGameBidData *passerBid;

/// Get current player bid.
-(PSGameBidData*)bidForPlayerId:(PSPlayerId)identifier;

/// Set player bids.
-(void)setBid:(PSBid*)bid forPlayer:(PSPlayerId)identifier withMax:(PSBid*)maxBid;

/// Set player current bid.
-(void)setCurrentBid:(PSBid*)bid forPlayer:(PSPlayerId)identifier;

/// Set player max bid.
-(void)setMaxBid:(PSBid*)bid forPlayer:(PSPlayerId)identifier;

@property (nonatomic, readonly) PSTrickCount contract;

@property (nonatomic, readwrite) uint8_t whisters;

/// Input options for the human player.
@property (nonatomic, readonly) PSGameInputOptions *options;

@property (nonatomic, readonly) PSStatistics *statistics;

@property (nonatomic, readonly) PSPlayerData *player;

/// Get current trump.
@property (nonatomic, readonly) PSCardSuit trump;

-(PSTrickCount)estimateHumanTrickCountWithSettings:(PSGameSettings*)settings;

-(PSBid*)estimateHumanContract;

-(PSScore*)scoreForPlayerId:(PSPlayerId)playerId;

-(PSPlayer*)nextPlayer:(PSPlayer*)player;

-(PSPlayer*)previousPlayer:(PSPlayer*)player;

-(void)resetConsecutivePassingSets;

-(void)incrementConsecutivePassingSets;

/// Get opponent player number tricks.
-(PSTrickCount)opponentTricks;

/// Get passer player number tricks.
-(PSTrickCount)passerTricks;

/// Get player number of tricks.
-(PSTrickCount)tricksOfPlayer:(PSPlayerId)identifier;

/// Set tricks of player.
-(void) setTricks:(PSTrickCount)tricks ofPlayer:(PSPlayerId)identifier;

/// update tricks of player by provided delta.
-(void) addTricks:(PSTrickCount)tricks ofPlayer:(PSPlayerId)identifier;

@end

/// A protocol for communicating game events to the hosting environment.
@protocol PSGameDelegate <NSObject>

/// A resume of the game has been requested.
/// @param game The game object.
@required -(void)requestedResumeGame:(PSGame*)game;

/// A resume of the game has started.
/// @param game The game object.
@required -(void)willResumeGame:(PSGame*)game;

/// The game has resumed.
/// @param game The game object.
@required -(void)didResumeGame:(PSGame*)game;

/// Dealing of the cards has finished.
/// @param game The game object.
@required -(void)didFinishDeal:(PSGame*)game;

/// Bidding on the game has started.
/// @param game The game object.
@required -(void)willStartBidding:(PSGame*)game;

/// A player has placed a bid.
/// @param player The player who placed the bid.
/// @param bidData The bid of the player.
@required -(void)player:(PSPlayer*)player didPlaceBid:(PSGameBidData*)bidData;

/// The first time a human bids in this round of the game.
/// @param game The game object.
/// @param player The player who placed the bid.
@required -(void)game:(PSGame*)game firstHumanBidByPlayer:(PSPlayer*)player;

/// The bidding has finished for the game.
/// @param game The game object.
@required -(void)didFinishBidding:(PSGame*)game;

/// Contracting has ended for the game.
/// @param game The game object.
@required -(void)didFinishContracting:(PSGame*)game;

/// The talon of the game has been shown.
/// @param game The game object.
/// @param settings Game settings object.
@required -(void)game:(PSGame*)game didShowTalonWithSettings:(PSGameSettings*)settings;

/// The talon of the game has been taken.
/// @param game The game object.
/// @param settings Game settings object.
@required -(void)game:(PSGame*)game didTakeTalonWithSettings:(PSGameSettings*)settings;

/// The player has declared his contract.
/// @param player The player who declared the contract.
/// @param bidData The contract declared by the player.
@required -(void)player:(PSPlayer *)player didDeclareContract:(PSGameBidData *)bidData;

/// The player who declared the contract is about to drop two cards.
/// @param game The game object.
@required -(void)willDrop:(PSGame*)game;

/// The player who declared the contract has dropped two cards.
/// @param game The game object.
/// @param player The player who discarded two cards.
/// @param settings The game settings object.
@required -(void)didDrop:(PSGame*)game forPlayer:(PSPlayer*)player withSettings:(PSGameSettings*)settings;

/// The open/closed play type has been selected.
/// @param game The game object.
/// @param settings The game settings object.
/// @param player1 The first player.
/// @param player2 The second player.
@required -(void)game:(PSGame *)game didSelectOpenTypeWithSettings:(PSGameSettings*)settings
         forPlayerOne:(PSPlayer*)player1
         andPlayerTwo:(PSPlayer*)player2;

/// The round of player moves has started.
/// @param game The game object.
@required -(void)didStartMovignRound:(PSGame *)game;

/// A player has played a card.
/// @param game The game object.
/// @param player The player who played the card.
/// @param card The card that has been played.
@required -(void)game:(PSGame *)game player:(PSPlayer*)player didPlayCard:(PSCard*)card;

/// The moving round will end with the move of the specific player.
/// @param game The game object.
/// @param player The player who played the last card in the round.
@required -(void)game:(PSGame*)game movingRoundWillEndWithPlayer:(PSPlayer*)player;

/// The moving round has ended in a win of a specified player.
/// @param game The game object.
/// @param settings The game settings object.
/// @param winner The player who won this round.
@required -(void)game:(PSGame*)game movingRoundDidEndWithSettings:(PSGameSettings*)settings
            andWinner:(PSPlayer*)winner;

/// Before calculating the score.
/// @param game The game object.
@required -(void)willScoreTheGame:(PSGame*)game;

/// After calculating the score.
/// @param game The game object.
@required -(void)didScoreTheGame:(PSGame*)game;

/// A talon card has been played in a game of all passes.
/// @param game The game object.
/// @param settings The game settings object.
/// @param round The number of the round in which the card has been played.
@required -(void)game:(PSGame*)game didPlayTalonCardWithSettings:(PSGameSettings*)settings
              inRound:(int)round;

/// A set is over in a game.
/// @param game The game object.
@required -(void)setIsOverInGame:(PSGame*)game;

/// The game is over.
/// @param game The game object.
@required -(void)gameIsOver:(PSGame*)game;

/// The initial position in a game is shown.
/// @param game The game object.
/// @param settings The game settings object.
@required -(void)willShowInitialPositionInGame:(PSGame*)game witSettings:(PSGameSettings*)settings;

/// A player's offer has been declined.
/// @param game The game object.
@required -(void)offerIsDeclined:(PSGame*)game;

/// A player's cards were opened.
/// @param game The game object.
/// @param playerId The id of the player whose cards were opened.
/// @param settings The game settings object.
@required -(void)game:(PSGame *)game didOpenCardsOfPlayer:(int)playerId
         withSettings:(PSGameSettings*)settings;

/// The check for ten tricks has started.
/// @param game The game object.
@required -(void)willCheckTrickingTen:(PSGame*)game;

/// The check for ten tricks has finished.
/// @param game The game object.
@required -(void)didCheckTrickingTen:(PSGame*)game;

/// The check for ten tricks has been requested.
/// @param game The game object.
@required -(void)requestedCheckTrickingTen:(PSGame*)game;

/// The fast-moving mode will be entered.
/// @param game The game object.
@required -(void)willEnterFastMove:(PSGame*)game;

/// The fast-moving mode has been entered.
/// @param game The game object.
@required -(void)didEnterFastMove:(PSGame*)game;

/// The fast-moving mode has been requested.
/// @param game The game object.
@required -(void)requestedFastMove:(PSGame*)game;

/// The replay mode will be started.
/// @param game The game object.
@required -(void)willStartReplay:(PSGame*)game;

/// The replay mode has finished.
/// @param game The game object.
@required -(void)didEndReplay:(PSGame*)game;

@end

/// A protocol for communicating game events to a resolver.
@protocol PSGameResolver <NSObject>

/// Initialize the resolver for the current state of the game.
/// @param game The game object.
@required -(void)resolverPrepareForGame:(PSGame*)game;

/// Reset the resolver for the current state of the game.
/// @param game The game object.
@required -(void)resolverResetForGame:(PSGame*)game;

/// Request the best score from the resolver.
/// @param game The game object.
@required -(void)resolverRequestBestScore:(PSGame*)game;

/// Request a solution from the resolver.
/// @param game The game object.
/// @param player The player for whom a solution is requested.
@required -(void)resolverRequestSolution:(PSGame*)game forPlayer:(PSPlayer*)player;

/// Inform the resolver of a move.
/// @param game The game object.
/// @param move The move to give the resolver.
@required -(void)resolverAdvance:(PSGame*)game withMove:(PSCardHash)move;

@end
